---------------------------------------------------------------------------------- 
-- Engineer: David McNamara
-- 
-- Module Name:    uart_tx - Behavioral 
-- Project Name: 
-- Target Devices: 
-- Tool versions: 
-- Description: Is the single byte uart transmiter

----------------------------------------------------------------------------------

library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;

entity uart_tx is

  port(
    clk, reset, tx_start : in  std_logic;
    tx_done              : out std_logic := '1';
    tx                   : out std_logic := '1';
    data_in              : in  std_logic_vector(7 downto 0)
    );
end uart_tx;

architecture arch of uart_tx is

  --state variables
  type   state_type is (transmit_wait, transmit_start, transmit_data, transmit_stop);
  signal current_state, next_state : state_type := transmit_wait;

  signal baud_clk_cnt_current, baud_clk_cnt_next : unsigned(3 downto 0);
  signal bit_cnt_current, bit_cnt_next           : unsigned(2 downto 0);
  signal tx_buffer_current, tx_buffer_next       : std_logic_vector(7 downto 0) := X"FF";
  signal tx_sig_current, tx_sig_next             : std_logic;
  signal uart_clk                                : std_logic;

  --clock divider to match 9600 baud with a 50MHz clock
  component ucg is
    port (clk      : in  std_logic;
          reset    : in  std_logic;
          uart_clk : out std_logic);
  end component;


begin


  --clock divider to match 9600 baud with a 50MHz clock
  u1 : ucg
    port map(clk      => clk,
             reset    => reset,
             uart_clk => uart_clk);


  process(clk, reset) 
  begin
    if (reset = '1') then
      current_state        <= transmit_wait;
      baud_clk_cnt_current <= (others => '0');
      bit_cnt_current      <= (others => '0');
      tx_buffer_current    <= (others => '0');
      tx_sig_current       <= '1';
    elsif (clk'event and clk = '1') then
      --udpate state and counter values
      current_state        <= next_state;
      baud_clk_cnt_current <= baud_clk_cnt_next;
      bit_cnt_current      <= bit_cnt_next;
      tx_buffer_current    <= tx_buffer_next;
      tx_sig_current       <= tx_sig_next;
    end if;
  end process;

-- next state logic
  process (data_in, current_state, baud_clk_cnt_current, bit_cnt_current, tx_buffer_current, uart_clk, tx_sig_current, tx_start)
  begin
    
    next_state        <= current_state;
    baud_clk_cnt_next <= baud_clk_cnt_current;
    bit_cnt_next      <= bit_cnt_current;
    tx_buffer_next    <= tx_buffer_current;
    tx_sig_next       <= tx_sig_current;
    tx_done           <= '0';

    case current_state is

      --transmit wait state, waits for a tc_start high signal
      when transmit_wait =>
        tx_sig_next <= '1';
        if (tx_start = '1') then
          next_state        <= transmit_start;
          baud_clk_cnt_next <= (others => '0');
          tx_buffer_next    <= data_in;  --load transmit byte
        end if;

        --transmit start (first bit goes to 0)
      when transmit_start =>
        tx_sig_next <= '0';
        if (uart_clk = '1') then
          if (baud_clk_cnt_current = 15) then  --count to 15
            next_state        <= transmit_data;
            baud_clk_cnt_next <= (others => '0');
            bit_cnt_next      <= (others => '0');
          else
            baud_clk_cnt_next <= baud_clk_cnt_current + 1;
          end if;
        end if;

        --transmit current bit values            
      when transmit_data =>
        tx_sig_next <= tx_buffer_current(0);
        if (uart_clk = '1') then
          if (baud_clk_cnt_current = 15) then  --count to 15
            baud_clk_cnt_next <= (others => '0');
            tx_buffer_next    <= '0' & tx_buffer_current(7 downto 1);
            if (bit_cnt_current = 7) then      --count to 7
              next_state <= transmit_stop;
            else
              bit_cnt_next <= bit_cnt_current + 1;
            end if;
          else
            baud_clk_cnt_next <= baud_clk_cnt_current + 1;
          end if;
        end if;

        --transmit stop state            
      when transmit_stop =>
        tx_sig_next <= '1';
        if (uart_clk = '1') then
          if (baud_clk_cnt_current = 15) then  --count to 15
            next_state <= transmit_wait;
            tx_done    <= '1';
          else
            baud_clk_cnt_next <= baud_clk_cnt_current + 1;
          end if;
        end if;
    end case;

  end process;

  tx <= tx_sig_current;                 --send current bit to tx pin

end arch;
